import 'dart:async';

import 'package:flight_search/fade_route.dart';
import 'package:flight_search/price_tab/animated_dot.dart';
import 'package:flight_search/price_tab/animated_plane_icon.dart';
import 'package:flight_search/price_tab/flight_stop.dart';
import 'package:flight_search/price_tab/flight_stop_card.dart';
import 'package:flight_search/ticket_page/tickets_page.dart';
import 'package:flutter/material.dart';

class PriceTab extends StatefulWidget {
  final double height;
  final VoidCallback onPlaneFlightStart;

  const PriceTab({super.key, required this.height, required this.onPlaneFlightStart});

  @override
  _PriceTabState createState() => _PriceTabState();
}

class _PriceTabState extends State<PriceTab> with TickerProviderStateMixin {
  final double _initialPlanePaddingBottom = 16.0;
  final double _minPlanePaddingTop = 16.0;
  final List<FlightStop> _flightStops = [
    FlightStop("JFK", "ORY", "JUN 05", "6h 25m", "\$851", "9:26 am - 3:43 pm"),
    FlightStop("MRG", "FTB", "JUN 20", "6h 25m", "\$532", "9:26 am - 3:43 pm"),
    FlightStop("ERT", "TVS", "JUN 20", "6h 25m", "\$718", "9:26 am - 3:43 pm"),
    FlightStop("KKR", "RTY", "JUN 20", "6h 25m", "\$663", "9:26 am - 3:43 pm"),
  ];
  final List<GlobalKey<FlightStopCardState>> _stopKeys = [];

  late final AnimationController _planeSizeAnimationController;
  late final AnimationController _planeTravelController;
  late final AnimationController _dotsAnimationController;
  late final AnimationController _fabAnimationController;

  @override
  void dispose() {
    _planeSizeAnimationController.dispose();
    _planeTravelController.dispose();
    _dotsAnimationController.dispose();
    _fabAnimationController.dispose();
    super.dispose();
  }

  late final Animation<double> _planeSizeAnimation;
  late final Animation<double> _planeTravelAnimation;
  late final Animation<double> _fabAnimation;

  List<Animation<double>> _dotPositions = [];

  double get _planeTopPadding =>
      _minPlanePaddingTop +
      (1 - _planeTravelAnimation.value) * _maxPlaneTopPadding;

  double get _maxPlaneTopPadding =>
      widget.height -
      _minPlanePaddingTop -
      _initialPlanePaddingBottom -
      _planeSize;

  double get _planeSize => _planeSizeAnimation.value;

  @override
  void initState() {
    super.initState();
    _initSizeAnimations();
    _initPlaneTravelAnimations();
    _initDotAnimationController();
    _initDotAnimations();
    _initFabAnimationController();
    _flightStops
        .forEach((stop) => _stopKeys.add(GlobalKey<FlightStopCardState>()));
    _planeSizeAnimationController.forward();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      child: Stack(
        alignment: Alignment.center,
        children: <Widget>[_buildPlane()]
          ..addAll(_flightStops.map(_buildStopCard))
          ..addAll(_flightStops.map(_mapFlightStopToDot))
          ..add(_buildFab()),
      ),
    );
  }

  Widget _buildStopCard(FlightStop stop) {
    int index = _flightStops.indexOf(stop);
    double topMargin = _dotPositions[index].value -
        0.5 * (FlightStopCard.height - AnimatedDot.size);
    bool isLeft = index.isOdd;
    return Align(
      alignment: Alignment.topCenter,
      child: Padding(
        padding: EdgeInsets.only(top: topMargin),
        child: FlightStopCard(
          key: _stopKeys[index],
          flightStop: stop,
          isLeft: isLeft,
        ),
      ),
    );
  }

  Widget _mapFlightStopToDot(FlightStop stop) {
    int index = _flightStops.indexOf(stop);
    return AnimatedDot(
      animation: _dotPositions[index],
      color: Colors.red,
    );
  }

  Widget _buildPlane() {
    return AnimatedBuilder(
      animation: _planeTravelAnimation,
      builder: (context, child) => Positioned(
        top: _planeTopPadding,
        child: Column(
          children: <Widget>[
            AnimatedPlaneIcon(animation: _planeSizeAnimation),
            Container(height: 40.0),
          ],
        ),
      ),
    );
  }

  Widget _buildFab() {
    return Positioned(
      bottom: 16.0,
      child: ScaleTransition(
        scale: _fabAnimation,
        child: FloatingActionButton(
          onPressed: () => _onFabPressed(),
          child: const Icon(Icons.check),
        ),
      ),
    );
  }

  Future _onFabPressed() async {
    widget.onPlaneFlightStart();
    for (GlobalKey<FlightStopCardState> stopKey in _stopKeys) {
      stopKey.currentState?.runAnimation();
      await Future.delayed(const Duration(milliseconds: 250));
    }
    Navigator.push(
      context,
      FadeRoute(builder: (context) => const TicketsPage()),
    );
  }

  void _initSizeAnimations() {
    _planeSizeAnimationController = AnimationController(
      duration: const Duration(milliseconds: 340),
      vsync: this,
    );
    _planeSizeAnimation = Tween<double>(
      begin: 60.0,
      end: 36.0,
    ).animate(CurvedAnimation(
      parent: _planeSizeAnimationController,
      curve: Curves.easeOut,
    ));
  }

  void _initPlaneTravelAnimations() {
    _planeTravelController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 400),
    );
    _planeTravelAnimation = CurvedAnimation(
      parent: _planeTravelController,
      curve: Curves.fastOutSlowIn,
    );
  }

  void _initDotAnimationController() {
    _dotsAnimationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 800),
    );
  }

  void _initDotAnimations() {
    final double maxDotTopMargin = widget.height - 24.0;
    for (int i = 0; i < _flightStops.length; i++) {
      double start = i * 0.1;
      double duration = 0.8;
      double end = duration + start;
      _dotPositions.add(
        Tween<double>(
          begin: maxDotTopMargin - 32.0,
          end: 0.0,
        ).animate(
          CurvedAnimation(
            parent: _dotsAnimationController,
            curve: Interval(start, end, curve: Curves.easeOut),
          ),
        ),
      );
    }
  }

  void _initFabAnimationController() {
    _fabAnimationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 300),
    );
    _fabAnimation = CurvedAnimation(
      parent: _fabAnimationController,
      curve: Curves.easeOut,
    );
  }
}
